P10-1. Grafana Tempo와 메시 분산 추적 - 이론 개념

Part 10 (이론): LGTM 트레이스 파이프라인 — Grafana Tempo와 메시 분산 추적 (OTLP · W3C Trace Context · TraceQL)

핵심 학습 목표

  1. 메트릭(Part 08)과 로그(Part 09)만으로는 여러 마이크로서비스를 거치는 요청의 병목 구간을 찾기 어려운 이유와, 분산 추적(Distributed Tracing) 이 이를 어떻게 해결하는지 설명할 수 있다.
  2. 분산 추적의 핵심 단위인 Trace, Span, Trace ID, Span ID, Parent Span ID 의 관계(부모-자식 폭포수 구조)를 택배 운송장 비유로 완벽히 이해한다.
  3. 서비스 메시(Waypoint / Envoy)가 코드 수정 없이 자동으로 해주는 일(Span 생성 · OTLP 전송 · 헤더 주입)과, 여러 홉을 이어 붙이는 표준 규약인 W3C Trace Context(traceparent) 및 B3 헤더의 역할을 구분한다.
  4. 차세대 트레이스 저장소인 Grafana Tempo 의 특징과 TraceQL 쿼리 문법을 익힌다.


1. 왜 메트릭과 로그만으로는 부족할까? (분산 추적의 탄생)

고객이 쇼핑몰 앱에서 [결제하기] 버튼을 눌렀는데 화면이 5초 동안 멈췄다가 겨우 완료되었다고 해봅시다.
이 요청 1건이 처리되는 동안 내부에서는 프론트엔드 → 주문 API(order-api) → 재고 확인 API → 결제 API → 외부 PostgreSQL DB 까지 총 5개의 서버를 차례대로 거쳤습니다.

이때 우리가 메트릭만 보면 "아, 전체 응답 시간이 5초 걸렸구나" 는 알 수 있지만, 5개의 서버 중 누가 4.9초를 잡아먹은 범인인지 단번에 알기 어렵습니다. 그렇다고 5개 서버의 수만 줄짜리 로그를 다 열어서 "방금 그 철수 고객의 요청 로그가 어디 있지?" 하고 눈으로 대조하는 것은 사막에서 바늘 찾기입니다.

이 문제를 해결하기 위해 등장한 것이 바로 고객의 요청이 정문에 들어오는 순간 가슴에 고유한 바코드 스티커(Trace ID)를 딱 붙여서, 그 요청이 거쳐 가는 모든 프록시와 서버마다 출입 시각과 퇴장 시각을 도장으로 찍어 한 장의 엑스레이 사진(Waterfall 차트)으로 조립해 주는 기술, 즉 분산 추적(Distributed Tracing) 입니다!


2. 국제 택배 운송장으로 이해하는 Trace 와 Span 의 구조

분산 추적을 처음 배울 때 나오는 5가지 핵심 용어는 국제 택배 배송 조회 화면을 떠올리면 100% 이해됩니다.

flowchart TB
    subgraph TRACE["하나의 전체 Trace (Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736 · 총 소요시간: 3,015ms)"]
        S1["[Root Span #1] study-client -> Waypoint
Span ID: aaa111 | Parent: 없음 (최초 출발!)
소요시간: 0ms ~ 3,015ms (전체 구간)"] S2["[Child Span #2] Waypoint -> order-api-v1
Span ID: bbb222 | Parent: aaa111
소요시간: 2ms ~ 3,012ms (내부 호출)"] S3["[Child Span #3] order-api-v1 -> pg-vm.mesh.internal:5432
Span ID: ccc333 | Parent: bbb222
소요시간: 5ms ~ 3,008ms (범인 검거: DB 쿼리가 3초 걸림!)"] S1 --> S2 --> S3 end
용어 정리 — 분산 추적 5대 핵심 개념 (Trace, Span, Trace ID, Span ID, Parent Span ID)

  1. Trace (트레이스 = 택배 1건의 전체 배송 여정):
    • 사용자의 요청 1건이 시스템에 들어와서 최종 응답이 나갈 때까지 거쳐 간 모든 구간의 기록을 하나로 묶은 전체 나무(Tree)입니다.
  2. Trace ID (트레이스 ID = 32자리 글로벌 운송장 번호):
    • 요청이 처음 시작될 때 발급되는 전 세계 유일무이한 16바이트(32글자 16진수, 예: 4bf92f3577b34da6a3ce929d0e0e4736) 번호입니다. 이 요청이 10개의 서버를 거치더라도 Trace ID는 끝까지 똑같은 번호를 유지합니다!
  3. Span (스팬 = 각 물류센터 구간별 처리 기록):
    • 전체 여정(Trace)을 이루는 각각의 단일 작업 조각입니다(예: "Waypoint가 요청을 받아 order-api로 전달한 구간").
    • 각 Span 안에는 구간 이름, 시작 시각, 걸린 시간(Duration), HTTP 상태 코드(http.status_code=200), 파드 이름 같은 상세 꼬리표(Attributes/Tags)가 들어 있습니다.
  4. Span ID 와 Parent Span ID (부모-자식 족보 번호):
    • 택배가 인천 물류센터(Span ID: aaa111)를 거쳐 대전 허브(Span ID: bbb222)로 갔다면, 대전 허브의 기록지에는 "내 직전 출발지(부모, Parent Span ID)는 aaa111번이다!" 라고 적혀 있습니다.
    • 이 Parent Span ID 덕분에 각 서버가 제각각 트레이스 조각(Span)을 Grafana Tempo로 던져도, Tempo가 퍼즐을 맞추듯 정확한 폭포수 계단 모양(Waterfall Chart) 으로 호출 순서를 재조립할 수 있습니다!


3. 서비스 메시는 어떻게 코드 수정 없이 트레이스를 만들어낼까? (OTLP & 헤더 전파)

원래 서비스 메시가 없을 때는 개발자가 모든 애플리케이션 코드마다 OpenTelemetry SDK 라이브러리를 설치하고 설정해야 트레이스가 나왔습니다.
하지만 Istio 서비스 메시(Waypoint / Envoy) 환경에서는 다음과 같이 인프라 수준에서 자동으로 트레이스(Span)를 생성하고 전송합니다!

sequenceDiagram
    autonumber
    participant Client as study-client 파드
    participant WP as Waypoint Proxy (Envoy L7)
    participant App as order-api 파드
    participant Alloy as Grafana Alloy (:4317)
    participant Tempo as Grafana Tempo (:3200)

    Client->>WP: 1. GET /trace-test 호출
    Note over WP: 2. 트레이스 헤더(traceparent, x-b3-traceid,
x-request-id) 자동 생성 및 스탬프 날인!
출발 시각 기록! WP->>App: 3. 헤더가 찍힌 요청을 order-api로 전달 App-->>WP: 4. 200 OK 응답 반환 (실제 echo 본문에 찍힌 헤더 확인 가능!) Note over WP: 5. 종료 시각 기록 후 Span 완성! WP--)Alloy: 6. 비동기 OTLP gRPC (:4317)로 Span 초고속 전송! Alloy--)Tempo: 7. Tempo 저장소로 전달 및 인덱싱 완료!
용어 정리 — W3C Trace Context (traceparent), B3 헤더, 그리고 OTLP

  1. W3C Trace Context (traceparent 헤더):
    • 과거에는 Zipkin의 x-b3-traceid 방식, Jaeger의 uber-trace-id 방식 등 회사마다 헤더 이름이 달라서 호환이 안 되었습니다. 이를 통일하기 위해 월드와이드웹 표준 기구(W3C)가 제정한 전 세계 공통 HTTP 트레이스 헤더가 바로 traceparent 입니다!
    • 형태: traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
      (버전(00) - 32자리 Trace ID - 16자리 Parent Span ID - 샘플링여부(01))
  2. 컨텍스트 헤더 전파 (Header Propagation — 실무 개발자와의 협업 포인트!):
    • Waypoint Proxy는 요청이 지나갈 때마다 자동으로 traceparent, x-request-id, x-b3-traceid 헤더를 만들어 붙여 주고 자기 구간의 Span을 Tempo로 보냅니다.
    • 단, 만약 order-api가 요청을 받은 뒤 또 다른 뒷단 API(payment-api)를 새로 호출한다면, 두 구간의 바코드가 끊어지지 않고 하나의 Trace ID로 묶이도록 order-api 코드에서 들어온 요청의 트레이스 헤더(traceparent, x-request-id)를 뒷단 요청 헤더에 그대로 복사해 넘겨주기(Header Forwarding) 만 하면 전체 체인이 하나로 완벽히 이어집니다!
  3. OTLP (OpenTelemetry Protocol, 포트 4317 / 4318):
    • Envoy/Waypoint가 생성한 트레이스 데이터를 수집기(Grafana Alloy)로 쏴줄 때 사용하는 국제 표준 전송 규약입니다. 고속 바이너리 통신인 gRPC 방식은 항상 4317번 포트, HTTP 방식은 4318번 포트를 사용합니다.


4. 왜 Jaeger 대신 Grafana Tempo 인가? (TraceQL)

과거에는 분산 추적 저장소로 Jaeger나 Zipkin을 많이 썼지만, 이들은 대용량 환경에서 무거운 Elasticsearch나 Cassandra DB를 백엔드로 요구했습니다.
반면 Grafana Tempo 는 앞서 배운 Grafana Loki와 똑같은 철학으로 설계되었습니다:


🔗 다음 단계 (실습 검증)

이론으로 배운 LGTM의 세 번째 기둥인 Grafana Tempo (2.7.1) 와 Istio OpenTelemetry 트레이싱 파이프라인을 클러스터에서 직접 검증해 봅니다!

  1. Istio meshConfig에 otel-tracing 프로바이더(alloy.monitoring.svc.cluster.local:4317)를 등록하고 Telemetry 리소스로 샘플링 비율을 100%로 켭니다.
  2. order-api가 되돌려주는 응답 본문 속에서 Waypoint가 자동으로 찍어 넣은 x-b3-traceid / x-request-id 헤더 실물을 눈으로 확인하고,
  3. Tempo의 검색 API(/api/search)와 상세 조회 API(/api/traces/<TraceID>)를 호출해 실제 생성된 Trace ID와 Span 데이터를 100% 실측합니다!

네비게이션

이전 목록 다음
◀ Part 09 실습 목록으로 Part 10 실습 ▶